/* * Copyright 2013 Guillaume Nodet, Harald Wellmann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * * See the License for the specific language governing permissions and * limitations under the License. */ package org.ops4j.pax.cdi.extender.impl; import static org.ops4j.pax.cdi.spi.Constants.EXTENDER_CAPABILITY; import static org.ops4j.pax.cdi.spi.BeanBundles.findExtensions; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.CDIProvider; import org.ops4j.pax.cdi.spi.CdiContainer; import org.ops4j.pax.cdi.spi.CdiContainerFactory; import org.ops4j.pax.cdi.spi.CdiWebAdapter; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.ServiceReference; import org.osgi.framework.wiring.BundleWire; import org.osgi.framework.wiring.BundleWiring; import org.osgi.util.tracker.BundleTracker; import org.osgi.util.tracker.BundleTrackerCustomizer; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements the PAX CDI extender capability. This extender depends on a {@link CDIProvider}, a * {@link CdiContainerFactory} and and optional CDI web adapter, represented by a * {@link CdiWebAdapter} with the property {@code type} set to {@code web}. * <p> * The extender creates a CDI container for each bean bundle. For web beans bundles, the CDI * container is not created until the web adapter is available. * * @author Guillaume Nodet * @author Harald Wellmann * */ public class CdiExtender implements BundleTrackerCustomizer<CdiContainer>, ServiceTrackerCustomizer<CdiWebAdapter, CdiWebAdapter> { private static Logger log = LoggerFactory.getLogger(CdiExtender.class); private final BundleContext context; private final CdiContainerFactory factory; private final CDIProvider cdiProvider; private BundleTracker<CdiContainer> bundleWatcher; private ServiceTracker<CdiWebAdapter, CdiWebAdapter> listenerTracker; private CdiWebAdapter webAdapter; private Map<Long, Bundle> webBundles = new HashMap<>(); CdiExtender(BundleContext context, CdiContainerFactory factory) { this.context = context; this.factory = factory; this.cdiProvider = new BundleCdiProvider(factory); } synchronized void start() { CDI.setCDIProvider(cdiProvider); log.info("starting CDI extender {}", context.getBundle().getSymbolicName()); this.listenerTracker = new ServiceTracker<>(context, CdiWebAdapter.class, this); this.listenerTracker.open(); this.bundleWatcher = new BundleTracker<>(context, Bundle.ACTIVE, this); this.bundleWatcher.open(); } synchronized void stop() { BundleCdi.dispose(); log.info("stopping CDI extender {}", context.getBundle().getSymbolicName()); bundleWatcher.close(); this.listenerTracker.close(); } @Override public synchronized CdiContainer addingBundle(final Bundle bundle, BundleEvent event) { boolean wired = false; List<BundleWire> wires = bundle.adapt(BundleWiring.class).getRequiredWires(EXTENDER_CAPABILITY); if (wires != null) { for (BundleWire wire : wires) { if (wire.getProviderWiring().getBundle().equals(context.getBundle())) { wired = true; break; } } } if (wired) { log.debug("found bean bundle: {}", bundle.getSymbolicName()); return createContainer(bundle); } else { log.trace("not a bean bundle: {}", bundle.getSymbolicName()); return null; } } @Override public void modifiedBundle(Bundle bundle, BundleEvent event, CdiContainer object) { // We don't care about state changes } @Override public synchronized void removedBundle(Bundle bundle, BundleEvent event, CdiContainer container) { if (container != null) { synchronized (container) { container.stop(); } } factory.removeContainer(bundle); if (webAdapter != null) { webAdapter.preDestroy(container); } } private CdiContainer createContainer(Bundle bundle) { // check if this is a web bundle Dictionary<String, String> headers = bundle.getHeaders(); String contextPath = headers.get("Web-ContextPath"); boolean isWebBundle = (contextPath != null); CdiContainer container = null; // Web bundles require a web adapter. if (isWebBundle) { // If the adapter is not available, just remember the bundle if (webAdapter == null) { log.debug("waiting for web adapter for {}", bundle); webBundles.put(bundle.getBundleId(), bundle); } // otherwise, create the CDI container, but don't start it until the servlet // context is available else { container = doCreateLazyContainer(bundle); } } // Standalone containers are started right now. else { container = doCreateContainer(bundle); container.start(new Object()); } return container; } private CdiContainer doCreateContainer(Bundle bundle) { // Find extensions Set<Bundle> extensions = new HashSet<>(); findExtensions(bundle, extensions); log.info("creating CDI container for bean bundle {} with extension bundles {}", bundle, extensions); return factory.createContainer(bundle, extensions); } private CdiContainer doCreateLazyContainer(Bundle bundle) { DelegatingCdiContainer container = new DelegatingCdiContainer(factory, bundle); if (webAdapter != null) { webAdapter.postCreate(container); } return container; } @Override public synchronized CdiWebAdapter addingService(ServiceReference<CdiWebAdapter> reference) { CdiWebAdapter service = context.getService(reference); if (webAdapter == null) { setWebAdapter(service); } return service; } @Override public void modifiedService(ServiceReference<CdiWebAdapter> reference, CdiWebAdapter service) { } @Override public synchronized void removedService(ServiceReference<CdiWebAdapter> reference, CdiWebAdapter service) { if (webAdapter == service) { setWebAdapter(listenerTracker.getService()); } context.ungetService(reference); } private void setWebAdapter(CdiWebAdapter listener) { log.debug("adding web adapter"); this.webAdapter = listener; for (Bundle bundle : webBundles.values()) { doCreateLazyContainer(bundle); } webBundles.clear(); } }